package com.atguigu.gmall.product.service.impl;

import com.atguigu.gmall.product.service.TestService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.configuration.Lock;
import org.apache.commons.lang.StringUtils;
import org.apache.http.impl.client.DefaultRedirectStrategy;
import org.redisson.api.RLock;
import org.redisson.api.RReadWriteLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Service;

import java.util.Arrays;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

@Slf4j
@Service
public class TestServiceImpl implements TestService {

    @Autowired
    private StringRedisTemplate redisTemplate;

    @Autowired
    private RedissonClient redissonClient;

    private static final String lock = "lock";
    private static final String lock_value = "lock";


//    //本地锁
//    @Override
//    public synchronized void testLock() {
//        // 查询Redis中的num值
//        String value = (String)this.redisTemplate.opsForValue().get("num");
//        // 没有该值return
//        if (StringUtils.isBlank(value)){
//            return ;
//        }
//        // 有值就转成成int
//        int num = Integer.parseInt(value);
//        // 把Redis中的num值+1
//        this.redisTemplate.opsForValue().set("num", String.valueOf(++num));
//    }

    //分布式锁
//    @Override
//    public synchronized void testLock() throws InterruptedException {
//        //获取分布式锁，获取到分布式锁以后在执行业务逻辑，设置锁过期时间
//        Boolean flag = redisTemplate.opsForValue().setIfAbsent(lock, lock_value);
//        if (flag){
//            try {
//                //业务逻辑：查询Redis中的num值
//                String value = (String)this.redisTemplate.opsForValue().get("num");
//                // 没有该值return
//                if (StringUtils.isBlank(value)){
//                    return ;
//                }
//                // 有值就转成成int
//                int num = Integer.parseInt(value);
//                // 把Redis中的num值+1
//                this.redisTemplate.opsForValue().set("num", String.valueOf(++num));
//            } catch (Exception e) {
//                throw new RuntimeException(e);
//            } finally {
//                //释放锁
//                redisTemplate.delete(lock);
//            }
//        }else {
//            log.error("获取分布式锁失败，尝试下次获取");
//            //获取锁失败，自旋再次尝试
//            TimeUnit.MILLISECONDS.sleep(500);
//            this.testLock();
//        }
//    }

    //LUA脚本保证删除的原子性
//    @Override
//    public void testLock() throws InterruptedException {
//        String lock_value = UUID.randomUUID().toString().replaceAll("-", "");
//        Boolean flag = redisTemplate.opsForValue().setIfAbsent(lock,lock_value,5,TimeUnit.SECONDS);
//        if (flag){
//            try {
//                //业务逻辑：查询Redis中的num值
//                String value = (String)this.redisTemplate.opsForValue().get("num");
//                // 没有该值return
//                if (StringUtils.isBlank(value)){
//                    return ;
//                }
//                // 有值就转成成int
//                int num = Integer.parseInt(value);
//                // 把Redis中的num值+1
//                this.redisTemplate.opsForValue().set("num", String.valueOf(++num));
//            } catch (Exception e) {
//                throw new RuntimeException(e);
//            } finally {
////                //释放锁 判断当前redis中的值和线程锁的值是否一样 (非原子操作)
////                String redisLock = redisTemplate.opsForValue().get(lock);
////                if (lock_value.equals(redisLock)){
////                    redisTemplate.delete(lock);
////                }
//
//            //执行LUA脚本释放锁，保障删除的原子性
//                //创建脚本对象，封装LUA脚本语言
//                    //KEYS[1]:传入的锁的名称
//                    //ARGV[1]:当前线程锁的值 UUID
//                String luaText = "if redis.call(\"get\",KEYS[1]) == ARGV[1]\n" +
//                        "then\n" +
//                        "    return redis.call(\"del\",KEYS[1])\n" +
//                        "else\n" +
//                        "    return 0\n" +
//                        "end";
//                DefaultRedisScript<Long> script = new DefaultRedisScript<>();
//                script.setScriptText(luaText);
//                script.setResultType(Long.class);
//                //为脚本中的动态参数设置值
//                //执行LUA脚本
//                Long result = redisTemplate.execute(script, Arrays.asList(lock), new String[]{lock_value});
//                System.out.println(result);
//            }
//        }else {
//            log.error("获取分布式锁失败，尝试下次获取");
//            //获取锁失败，自旋再次尝试
//            Thread.sleep(100);
//            this.testLock();
//        }
//    }

    //使用Redisson 解决分布式锁
    @Override
    public void testLock() throws InterruptedException {
        //创建锁对象
        RLock rlock = redissonClient.getLock("lock");

        try {
            //获取锁：线程获取锁失败会一直等待获取到锁为止
            //lock()：默认锁时间30s，任务执行超时后，会执行‘看门狗’线程，检查业务执行情况，如果业务在执行，自动为锁续期
//            rlock.lock();

            //获取锁：设置过期时间，到时间自动释放锁
//            rlock.lock(5,TimeUnit.SECONDS);

            //获取锁：尝试获取锁
                //参数1：获取等待时间，超过该时间没有获取锁，则返回false
                //参数2：锁过期时间
                //参数3：时间单位
            boolean flag = rlock.tryLock(1, 5, TimeUnit.SECONDS);
            if (flag){
            //业务逻辑：查询Redis中的num值
                String value = (String)this.redisTemplate.opsForValue().get("num");
                // 没有该值return
                if (StringUtils.isBlank(value)){
                    return ;
                }
                // 有值就转成成int
                int num = Integer.parseInt(value);
                // 把Redis中的num值+1
                this.redisTemplate.opsForValue().set("num", String.valueOf(++num));

                //测试可重入
                this.check();

            }else {
                //如果场景要求线程业务必须都执行，加自旋
                TimeUnit.SECONDS.sleep(1);
                this.testLock();
            }

        } catch (NumberFormatException e) {
            throw new RuntimeException(e);
        } finally {
            //释放锁
            rlock.unlock();
        }
    }


    private void check() {
        RLock rLock = redissonClient.getLock("lock");
        try {
            rLock.lock();
            System.out.println("测试可重入");
        } catch (Exception e) {
            throw new RuntimeException(e);
        } finally {
            rLock.unlock();
        }
    }

    @Override
    public String read() {
        //创建读锁对象
        RReadWriteLock readWriteLock = redissonClient.getReadWriteLock("readWriteLock");
        //获取读锁
        RLock rLock = readWriteLock.readLock();
        //设置时间
        rLock.lock(10,TimeUnit.SECONDS);
        //从redis中读数据
        String msg = redisTemplate.opsForValue().get("msg");
        //释放锁
        rLock.unlock();
        return msg;
    }

    @Override
    public void write() {
        //1.创建读写锁对象
        RReadWriteLock readWriteLock = redissonClient.getReadWriteLock("readWriteLock");
        //2.获取写锁
        RLock lock = readWriteLock.writeLock();
        //加锁 给锁的有效期设置为10s
        lock.lock(10, TimeUnit.SECONDS);

        //3.业务将数据写入redis
        redisTemplate.opsForValue().set("msg", "msgData");

        //4.释放写锁
        //lock.unlock();
    }

}
